﻿
using Newtonsoft.Json;
using MNH.WebApps.Common.Extensions;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;

namespace MNH.WebApps.Common.Helpers.JqGrid
{
    public class JqGridSearch
    {
        private static readonly Dictionary<string, string> _whereOperation = new Dictionary<string, string>()
    {
      {
        "in",
        " {0} = @{1} "
      },
      {
        "eq",
        " {0} = @{1} "
      },
      {
        "ni",
        " {0} != @{1} "
      },
      {
        "ne",
        " {0} != @{1} "
      },
      {
        "lt",
        " {0} < @{1} "
      },
      {
        "le",
        " {0} <= @{1} "
      },
      {
        "gt",
        " {0} > @{1} "
      },
      {
        "ge",
        " {0} >= @{1} "
      },
      {
        "bw",
        " {0}.StartsWith(@{1}) "
      },
      {
        "bn",
        " !{0}.StartsWith(@{1}) "
      },
      {
        "ew",
        " {0}.EndsWith(@{1}) "
      },
      {
        "en",
        " !{0}.EndsWith(@{1}) "
      },
      {
        "cn",
        " {0}.Contains(@{1}) "
      },
      {
        "nc",
        " !{0}.Contains(@{1}) "
      }
    };
        private static readonly Dictionary<string, string> _validOperators = new Dictionary<string, string>()
    {
      {
        "Object",
        ""
      },
      {
        "Boolean",
        "eq:ne:"
      },
      {
        "Char",
        ""
      },
      {
        "String",
        "eq:ne:lt:le:gt:ge:bw:bn:cn:nc:"
      },
      {
        "SByte",
        ""
      },
      {
        "Byte",
        "eq:ne:lt:le:gt:ge:"
      },
      {
        "Int16",
        "eq:ne:lt:le:gt:ge:"
      },
      {
        "UInt16",
        ""
      },
      {
        "Int32",
        "eq:ne:lt:le:gt:ge:"
      },
      {
        "UInt32",
        ""
      },
      {
        "Int64",
        "eq:ne:lt:le:gt:ge:"
      },
      {
        "UInt64",
        ""
      },
      {
        "Decimal",
        "eq:ne:lt:le:gt:ge:"
      },
      {
        "Single",
        "eq:ne:lt:le:gt:ge:"
      },
      {
        "Double",
        "eq:ne:lt:le:gt:ge:"
      },
      {
        "DateTime",
        "eq:ne:lt:le:gt:ge:"
      },
      {
        "TimeSpan",
        ""
      },
      {
        "Guid",
        ""
      }
    };
        private int _parameterIndex;

        public IQueryable<T> ApplyFilter<T>(IQueryable<T> query, bool _search, string searchField, string searchString, string searchOper, string filters, NameValueCollection form)
        {
            if (!_search)
                return query;
            if (!string.IsNullOrWhiteSpace(searchString) && !string.IsNullOrWhiteSpace(searchOper) && !string.IsNullOrWhiteSpace(searchField))
                return this.manageSingleFieldSearch<T>(query, searchField, searchString, searchOper);
            else
                return !string.IsNullOrWhiteSpace(filters) ? this.manageMultiFieldSearch<T>(query, filters) : this.manageToolbarSearch<T>(query, form);
        }

        private Tuple<string, object> getPredicate<T>(string searchField, string searchOper, string searchValue)
        {
            if (string.IsNullOrWhiteSpace(searchValue))
                return (Tuple<string, object>)null;
            Type fieldType = ReflectionHelper.FindFieldType(typeof(T), searchField, "", 3);
            if (fieldType == (Type)null)
                throw new InvalidOperationException(searchField + " is not defined.");
            if (!JqGridSearch._validOperators[fieldType.GenericTypeArguments.Length > 0 ? fieldType.GenericTypeArguments[0].Name : fieldType.Name].Contains(searchOper + ":"))
                return (Tuple<string, object>)null;

            // change it to miladi date
            object d;
            if ((fieldType.GenericTypeArguments.Length > 0 && fieldType.GenericTypeArguments[0].Name == "DateTime") || fieldType.Name.Contains("DateTime"))
            {

                searchValue = DateTimeHelper.ConvertToGeorgian(searchValue, false, searchOper == "le").ToString();

                Type t = Type.GetType("System.Nullable`1[[System.DateTime, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]");

                if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    if (String.IsNullOrEmpty(searchValue))
                        d = null;
                    else
                        d = Convert.ChangeType(searchValue, t.GetGenericArguments()[0]);
                }
                else
                {
                    d = Convert.ChangeType(searchValue, t);
                }
            }
            else
            {
                d = Convert.ChangeType((object)searchValue, fieldType);
            }

            object obj = d;//
            return new Tuple<string, object>(this.getSearchOperator(searchOper, searchField), obj);
        }

        private string getSearchOperator(string ruleSearchOperator, string searchField)
        {
            string format;
            if (!JqGridSearch._whereOperation.TryGetValue(ruleSearchOperator, out format))
                throw new NotSupportedException(string.Format("{0} is not supported.", (object)ruleSearchOperator));
            string str = string.Format(format, (object)searchField, (object)this._parameterIndex);
            ++this._parameterIndex;
            return str;
        }

        private IQueryable<T> manageMultiFieldSearch<T>(IQueryable<T> query, string filters)
        {
            SearchFilter searchFilter = JsonConvert.DeserializeObject<SearchFilter>(filters);
            string groupOp = searchFilter.groupOp;
            if (searchFilter.rules == null)
                return query;
            List<object> list = new List<object>();
            string str = string.Empty;
            foreach (SearchRule searchRule in searchFilter.rules)
            {
                Tuple<string, object> predicate = this.getPredicate<T>(searchRule.field, searchRule.op, searchRule.data);
                if (predicate != null)
                {
                    list.Add(predicate.Item2);
                    str = str + predicate.Item1 + " " + groupOp + " ";
                }
            }
            if (string.IsNullOrWhiteSpace(str))
                return query;
            string predicate1 = str.Remove(str.Length - groupOp.Length - 2);
            query = LinqExtensions.Where<T>(query, predicate1, list.ToArray());
            return query;
        }

        private IQueryable<T> manageSingleFieldSearch<T>(IQueryable<T> query, string searchField, string searchString, string searchOper)
        {
            Tuple<string, object> predicate = this.getPredicate<T>(searchField, searchOper, searchString);
            if (predicate != null)
                query = LinqExtensions.Where<T>(query, predicate.Item1, new object[1]
        {
          predicate.Item2
        });
            return query;
        }

        private IQueryable<T> manageToolbarSearch<T>(IQueryable<T> query, NameValueCollection form)
        {
            string str = string.Empty;
            List<object> list = new List<object>();
            foreach (string searchField in form.AllKeys)
            {
                if (!searchField.Equals("nd") && !searchField.Equals("sidx") && (!searchField.Equals("sord") && !searchField.Equals("page")) && (!searchField.Equals("rows") && !searchField.Equals("_search")) && form[searchField] != null)
                {
                    Tuple<string, object> predicate = this.getPredicate<T>(searchField, "eq", form[searchField]);
                    if (predicate != null)
                    {
                        list.Add(predicate.Item2);
                        str = str + predicate.Item1 + " And ";
                    }
                }
            }
            if (string.IsNullOrWhiteSpace(str))
                return query;
            string predicate1 = str.Remove(str.Length - 5);
            query = LinqExtensions.Where<T>(query, predicate1, list.ToArray());
            return query;
        }
    }
}
